#!/usr/bin/env python3
# Written by Adnan Shameem
# License: CC0 - do whatever you want with it

# This script relies on xfce-notifyd. It is lightweight and saves the
#  notifications in a ~/.cache/xfce4/notifyd/log file which is formatted
#  in INI type format.
# To make this script work,
# 1. Install notification-daemon
# 2. Install xfce4-notifyd
# 3. Run either:
# setsid /usr/lib/xfce4/notifyd/xfce4-notifyd
# or
# systemctl --user start xfce4-notifyd
# 4. Run xfce4-notifyd-config and go to Log and Turn ON Notifications
#  and then set Log notifications to "always"
# Now get some notifications from your apps.
# Run this script to show them as a sidebar list.

# If you want to toggle the panel, run this with a toggle script like:
#  `python-invert.sh` in the current dir or https://gitlab.com/snippets/1994036
# Something like ./python-invert.sh path/to/notification-sidebar.py
# It will invert the panel everytime this is run.
# You can then run this on a button on a panel.
# Would be great for lightweight desktops, such as, jwm, openbox, i3 etc.
#  where DE does not have a notification list panel available.

# Screenshot: https://imgur.com/wah27HZ

## -------- Imports -------- ##

# Python GTK3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

# Especially for getting screen dimensions
from gi.repository import Gdk

# For processing INI
try:
	from configparser import ConfigParser
except ImportError:
	from ConfigParser import ConfigParser  # ver. < 3.0

# For getting full path to home directory
import os
# For getting python version
import sys

# For processing time
from datetime import datetime, timedelta


# -------- Classes/Implementation -------- ##

def deletefile(filename):
	if os.path.isfile(filename):
		os.remove(filename)

# Get/expand real userpath from "~"
# We will keep the xfce notofications log
inipath = os.path.expanduser("~/.cache/xfce4/notifyd/log")

# Panel width
panel_width = 400

# Class to handle multiline list items
class ListBoxRowWithData(Gtk.ListBoxRow):
	def __init__(self, data1, data2):
		super(Gtk.ListBoxRow, self).__init__()
		self.data1 = data1
		self.data2 = data2

		box = Gtk.VBox(baseline_position=Gtk.BaselinePosition.TOP, spacing=0)
		item_line1 = Gtk.Label()
		item_line1.set_markup("<b>"+data1+"</b>")
		item_line1.set_line_wrap(True)
		box.pack_start(item_line1, False, False, 5)

		if data2 != '':
			item_line2 = Gtk.Label()
			item_line2.set_markup(data2)
			item_line2.set_line_wrap(True)
			box.pack_start(item_line2, False, False, 5)

		self.add(box)


class MyWindow(Gtk.Window):

	def __init__(self):
		Gtk.Window.__init__(self, title="Notifications")

		# Get screen dimensions
		display = Gdk.Display.get_default()
		monitor = display.get_monitor(0)
		swidth = monitor.get_geometry().width
		sheight = monitor.get_geometry().height

		# Size the window
		# Remove the titlebar because having it would not let us set the height
		# past titlebar.
		self.set_decorated(False)
		self.move(swidth - panel_width,0)
		self.set_default_size(panel_width, sheight)

		main_grid = Gtk.Grid()
		self.add(main_grid)

		btnquit = Gtk.Button(label="Close")
		btnquit.connect("clicked", Gtk.main_quit)

		btnclear = Gtk.Button(label="Clear")
		btnclear.connect("clicked", self.on_btnclear_clicked)

		notif_list = Gtk.ListBox()
		notif_scroller = Gtk.ScrolledWindow()

		# Read INI file
		inidata = ConfigParser()
		# The check is temporary. See TODO below.
		if sys.version[0] == '2':
			inidata.read(inipath)
		else:
			# Sometimes some weird characters appear on the INI.
			# ", encoding="ISO-8859-1" stops it from crashing.
			# This is a temoporary fix. TODO: Fix this issue for real.
			inidata.read(inipath, encoding="ISO-8859-1")

		# Get all sections
		sections = inidata.sections()
		# Reverse to show the latest ones first
		sections.sort(reverse=True)

		for section in sections:
			itemdate = datetime.strptime(section, '%Y-%m-%dT%H:%M:%S.%f')
			if itemdate.date() == datetime.today().date(): # Today
				itemdate = itemdate.strftime('%I:%M%p')
			elif itemdate.date() == (datetime.today() - timedelta(days=1)).date(): # Yesterday
				itemdate = itemdate.strftime('Yesterday %I:%M%p')
			else: # Not today, not yesterday, so show full date and time
				itemdate = itemdate.strftime('%Y-%m-%d %I:%M%p')
			notif_list.add(ListBoxRowWithData(itemdate+': '+inidata.get(section, 'summary'), inidata.get(section, 'body')))

		main_grid.attach(notif_scroller, 1, 0, 2, 1)
		notif_scroller.add(notif_list)
		main_grid.attach(btnquit, 1, 2, 1, 1)
		main_grid.attach_next_to(btnclear, btnquit, Gtk.PositionType.RIGHT, 1, 1)
		notif_list.set_hexpand(True)
		notif_list.set_vexpand(True)


	def on_btnclear_clicked(self, widget):
		deletefile(inipath)
		Gtk.main_quit()

win = MyWindow()
win.connect("destroy", Gtk.main_quit)
# Close this panel when mouse gets outside of window
# TODO: Does not work with JWM panel button. Fix it or implement click outside window.
#win.connect("leave-notify-event", Gtk.main_quit, "")
# Remove taskbar button
win.set_skip_taskbar_hint(True)
win.show_all()
# Remove titlebar but keep border
win.get_window().set_decorations(Gdk.WMDecoration.BORDER)
# Always on top
win.set_keep_above(True);

Gtk.main()
